package com.example.annotation;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import com.example.config.ShardRabbitListenerConfigUtils;
import com.example.listener.MethodShardRabbitListenerEndpoint;
import com.example.lock.ShardRabbitLockManager;
import com.example.moniter.ShardRabbitListenerManager;
import com.example.registry.ShardRabbitListenerEndpointRegistry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.Base64UrlNamingStrategy;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.Binding.DestinationType;
import org.springframework.amqp.rabbit.annotation.Argument;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurer;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.listener.MethodRabbitListenerEndpoint;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
import org.springframework.amqp.rabbit.listener.adapter.ReplyPostProcessor;
import org.springframework.amqp.rabbit.listener.api.RabbitListenerErrorHandler;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.Scope;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.Environment;
import org.springframework.core.task.TaskExecutor;
import org.springframework.lang.Nullable;
import org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

public class ShardRabbitListenerAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered, BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware, SmartInitializingSingleton {
    public static final String DEFAULT_RABBIT_LISTENER_CONTAINER_FACTORY_BEAN_NAME = "rabbitListenerContainerFactory";
    public static final String RABBIT_EMPTY_STRING_ARGUMENTS_PROPERTY = "spring.rabbitmq.emptyStringArguments";
    private static final ConversionService CONVERSION_SERVICE = new DefaultConversionService();
    private final Log logger = LogFactory.getLog(this.getClass());
    private final Set<String> emptyStringArguments = new HashSet();
    private RabbitListenerEndpointRegistry endpointRegistry;
    private String defaultContainerFactoryBeanName = DEFAULT_RABBIT_LISTENER_CONTAINER_FACTORY_BEAN_NAME;
    private BeanFactory beanFactory;
    private ClassLoader beanClassLoader;
    private final ShardRabbitListenerAnnotationBeanPostProcessor.RabbitHandlerMethodFactoryAdapter messageHandlerMethodFactory = new ShardRabbitListenerAnnotationBeanPostProcessor.RabbitHandlerMethodFactoryAdapter();
    private final RabbitListenerEndpointRegistrar registrar = new RabbitListenerEndpointRegistrar();
    private final AtomicInteger counter = new AtomicInteger();
    private final ConcurrentMap<Class<?>, ShardRabbitListenerAnnotationBeanPostProcessor.TypeMetadata> typeCache = new ConcurrentHashMap();
    private BeanExpressionResolver resolver = new StandardBeanExpressionResolver();
    private BeanExpressionContext expressionContext;
    private int increment;
    private Charset charset;

    public int getOrder() {
        return 2147483647;
    }

    public ShardRabbitListenerAnnotationBeanPostProcessor() {
        this.charset = StandardCharsets.UTF_8;
        this.emptyStringArguments.add("x-dead-letter-exchange");
    }

    public void setEndpointRegistry(RabbitListenerEndpointRegistry endpointRegistry) {
        this.endpointRegistry = endpointRegistry;
    }

    public void setContainerFactoryBeanName(String containerFactoryBeanName) {
        this.defaultContainerFactoryBeanName = containerFactoryBeanName;
    }

    public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory messageHandlerMethodFactory) {
        this.messageHandlerMethodFactory.setMessageHandlerMethodFactory(messageHandlerMethodFactory);
    }

    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        if (beanFactory instanceof ConfigurableListableBeanFactory) {
            this.resolver = ((ConfigurableListableBeanFactory)beanFactory).getBeanExpressionResolver();
            this.expressionContext = new BeanExpressionContext((ConfigurableListableBeanFactory)beanFactory, (Scope)null);
        }

    }

    public void setBeanClassLoader(ClassLoader classLoader) {
        this.beanClassLoader = classLoader;
    }

    public void setEnvironment(Environment environment) {
        String property = (String)environment.getProperty(RABBIT_EMPTY_STRING_ARGUMENTS_PROPERTY, String.class);
        if (property != null) {
            this.emptyStringArguments.addAll(StringUtils.commaDelimitedListToSet(property));
        }

    }

    public void setCharset(Charset charset) {
        this.charset = charset;
    }

    MessageHandlerMethodFactory getMessageHandlerMethodFactory() {
        return this.messageHandlerMethodFactory;
    }

    public void afterSingletonsInstantiated() {
        this.registrar.setBeanFactory(this.beanFactory);
        if (this.beanFactory instanceof ListableBeanFactory) {
            Map<String, RabbitListenerConfigurer> instances = ((ListableBeanFactory)this.beanFactory).getBeansOfType(RabbitListenerConfigurer.class);
            Iterator var2 = instances.values().iterator();

            while(var2.hasNext()) {
                RabbitListenerConfigurer configurer = (RabbitListenerConfigurer)var2.next();
                configurer.configureRabbitListeners(this.registrar);
            }
        }

        if (this.registrar.getEndpointRegistry() == null) {
            if (this.endpointRegistry == null) {
                Assert.state(this.beanFactory != null, "BeanFactory must be set to find endpoint registry by bean name");
                this.endpointRegistry = (RabbitListenerEndpointRegistry)this.beanFactory.getBean(ShardRabbitListenerConfigUtils.RABBIT_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME, ShardRabbitListenerEndpointRegistry.class);
            }

            this.registrar.setEndpointRegistry(this.endpointRegistry);
        }

        if (this.defaultContainerFactoryBeanName != null) {
            this.registrar.setContainerFactoryBeanName(this.defaultContainerFactoryBeanName);
        }

        MessageHandlerMethodFactory handlerMethodFactory = this.registrar.getMessageHandlerMethodFactory();
        if (handlerMethodFactory != null) {
            this.messageHandlerMethodFactory.setMessageHandlerMethodFactory(handlerMethodFactory);
        }

        this.registrar.afterPropertiesSet();
        ShardRabbitLockManager shardRabbitLockManager = (ShardRabbitLockManager)this.beanFactory.getBean(ShardRabbitListenerConfigUtils.RABBIT_LISTENER_LOCK_MANAGER_BEAN_NAME, ShardRabbitLockManager.class);
        String servieName = (String)this.resolveExpression("${spring.application.name}");
        shardRabbitLockManager.initTryAllLock(servieName, 1);
        this.typeCache.clear();
        ShardRabbitListenerConfigUtils.initDone = true;
        this.logger.info("ShardRabbitListenerConfigUtils.initDone");
    }

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetClass = AopUtils.getTargetClass(bean);
        ShardRabbitListenerAnnotationBeanPostProcessor.TypeMetadata metadata = (ShardRabbitListenerAnnotationBeanPostProcessor.TypeMetadata)this.typeCache.computeIfAbsent(targetClass, this::buildMetadata);
        ShardRabbitListenerAnnotationBeanPostProcessor.ListenerMethod[] var5 = metadata.listenerMethods;
        int var6 = var5.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            ShardRabbitListenerAnnotationBeanPostProcessor.ListenerMethod lm = var5[var7];
            ShardRabbitListener[] var9 = lm.annotations;
            int var10 = var9.length;

            for(int var11 = 0; var11 < var10; ++var11) {
                ShardRabbitListener rabbitListener = var9[var11];
                this.processAmqpListener(rabbitListener, lm.method, bean, beanName);
            }
        }

        return bean;
    }

    private ShardRabbitListenerAnnotationBeanPostProcessor.TypeMetadata buildMetadata(Class<?> targetClass) {
        Collection<ShardRabbitListener> classLevelListeners = this.findListenerAnnotations(targetClass);
        boolean hasClassLevelListeners = classLevelListeners.size() > 0;
        List<ShardRabbitListenerAnnotationBeanPostProcessor.ListenerMethod> methods = new ArrayList();
        List<Method> multiMethods = new ArrayList();
        ReflectionUtils.doWithMethods(targetClass, (method) -> {
            Collection<ShardRabbitListener> listenerAnnotations = this.findListenerAnnotations(method);
            if (listenerAnnotations.size() > 0) {
                methods.add(new ShardRabbitListenerAnnotationBeanPostProcessor.ListenerMethod(method, (ShardRabbitListener[])listenerAnnotations.toArray(new ShardRabbitListener[listenerAnnotations.size()])));
            }

        }, ReflectionUtils.USER_DECLARED_METHODS);
        return methods.isEmpty() && multiMethods.isEmpty() ? ShardRabbitListenerAnnotationBeanPostProcessor.TypeMetadata.EMPTY : new ShardRabbitListenerAnnotationBeanPostProcessor.TypeMetadata((ShardRabbitListenerAnnotationBeanPostProcessor.ListenerMethod[])methods.toArray(new ShardRabbitListenerAnnotationBeanPostProcessor.ListenerMethod[methods.size()]), (Method[])multiMethods.toArray(new Method[multiMethods.size()]), (ShardRabbitListener[])classLevelListeners.toArray(new ShardRabbitListener[classLevelListeners.size()]));
    }

    private Collection<ShardRabbitListener> findListenerAnnotations(AnnotatedElement element) {
        return (Collection)MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY).stream(ShardRabbitListener.class).map((ann) -> {
            return (ShardRabbitListener)ann.synthesize();
        }).collect(Collectors.toList());
    }

    protected void processAmqpListener(ShardRabbitListener rabbitListener, Method method, Object bean, String beanName) {
        int shardSize = Integer.parseInt((String)this.resolveExpression(rabbitListener.shardSize()));
        String queueNamePrefix = (String)this.resolveExpression(rabbitListener.queueNamePrefix());
        String instanceId = (String)this.resolveExpression("${spring.cloud.consul.discovery.instance-id}");
        ShardRabbitListenerManager shardRabbitListenerManager = (ShardRabbitListenerManager)this.beanFactory.getBean(ShardRabbitListenerConfigUtils.RABBIT_LISTENER_MANAGER_BEAN_NAME, ShardRabbitListenerManager.class);
        shardRabbitListenerManager.add(rabbitListener);

        for(int i = 0; i < shardSize; ++i) {
            Method methodToUse = this.checkProxy(method, bean);
            MethodShardRabbitListenerEndpoint endpoint = new MethodShardRabbitListenerEndpoint(queueNamePrefix, shardSize, i + 1, instanceId);
            endpoint.setMethod(methodToUse);
            String queueId = endpoint.getQueueNamePrefix() + "_" + endpoint.getNum();
            endpoint.setId(queueId);
            endpoint.setQueueNames(new String[]{queueId});
            this.processListener(endpoint, rabbitListener, bean, methodToUse, beanName);
        }

    }

    private Method checkProxy(Method methodArg, Object bean) {
        Method method = methodArg;
        if (AopUtils.isJdkDynamicProxy(bean)) {
            try {
                method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
                Class<?>[] proxiedInterfaces = ((Advised)bean).getProxiedInterfaces();
                Class[] var5 = proxiedInterfaces;
                int var6 = proxiedInterfaces.length;
                int var7 = 0;

                while(var7 < var6) {
                    Class iface = var5[var7];

                    try {
                        method = iface.getMethod(method.getName(), method.getParameterTypes());
                        break;
                    } catch (NoSuchMethodException var10) {
                        ++var7;
                    }
                }
            } catch (SecurityException var11) {
                ReflectionUtils.handleReflectionException(var11);
            } catch (NoSuchMethodException var12) {
                throw new IllegalStateException(String.format("@RabbitListener method '%s' found on bean target class '%s', but not found in any interface(s) for a bean JDK proxy. Either pull the method up to an interface or switch to subclass (CGLIB) proxies by setting proxy-target-class/proxyTargetClass attribute to 'true'", methodArg.getName(), methodArg.getDeclaringClass().getSimpleName()), var12);
            }
        }

        return method;
    }

    protected void processListener(MethodRabbitListenerEndpoint endpoint, ShardRabbitListener rabbitListener, Object bean, Object target, String beanName) {
        String queueNamePrefix = (String)this.resolveExpression(rabbitListener.queueNamePrefix());
        endpoint.setBean(bean);
        endpoint.setMessageHandlerMethodFactory(this.messageHandlerMethodFactory);
        endpoint.setConcurrency(this.resolveExpressionAsStringOrInteger(rabbitListener.concurrency(), "concurrency"));
        endpoint.setBeanFactory(this.beanFactory);
        endpoint.setReturnExceptions(this.resolveExpressionAsBoolean(rabbitListener.returnExceptions()));
        Object errorHandler = this.resolveExpression(rabbitListener.errorHandler());
        String priority;
        if (errorHandler instanceof RabbitListenerErrorHandler) {
            endpoint.setErrorHandler((RabbitListenerErrorHandler)errorHandler);
        } else {
            if (!(errorHandler instanceof String)) {
                throw new IllegalStateException("error handler mut be a bean name or RabbitListenerErrorHandler, not a " + errorHandler.getClass().toString());
            }

            priority = (String)errorHandler;
            if (StringUtils.hasText(priority)) {
                endpoint.setErrorHandler((RabbitListenerErrorHandler)this.beanFactory.getBean(priority, RabbitListenerErrorHandler.class));
            }
        }

        endpoint.setGroup(queueNamePrefix);
        endpoint.setAutoStartup(false);
        endpoint.setExclusive(rabbitListener.exclusive());
        priority = this.resolve(rabbitListener.priority());
        if (StringUtils.hasText(priority)) {
            try {
                endpoint.setPriority(Integer.valueOf(priority));
            } catch (NumberFormatException var10) {
                throw new BeanInitializationException("Invalid priority value for " + rabbitListener + " (must be an integer)", var10);
            }
        }

        this.resolveExecutor(endpoint, rabbitListener, target, beanName);
        this.resolveAdmin(endpoint, rabbitListener, target);
        this.resolveAckMode(endpoint, rabbitListener);
        this.resolvePostProcessor(endpoint, rabbitListener, target, beanName);
        RabbitListenerContainerFactory<?> factory = this.resolveContainerFactory(rabbitListener, target, beanName);
        this.registrar.registerEndpoint(endpoint, factory);
    }

    private void resolveAckMode(MethodRabbitListenerEndpoint endpoint, ShardRabbitListener rabbitListener) {
        String ackModeAttr = rabbitListener.ackMode();
        if (StringUtils.hasText(ackModeAttr)) {
            Object ackMode = this.resolveExpression(ackModeAttr);
            if (ackMode instanceof String) {
                endpoint.setAckMode(AcknowledgeMode.valueOf((String)ackMode));
            } else if (ackMode instanceof AcknowledgeMode) {
                endpoint.setAckMode((AcknowledgeMode)ackMode);
            } else {
                Assert.isNull(ackMode, "ackMode must resolve to a String or AcknowledgeMode");
            }
        }

    }

    private void resolveAdmin(MethodRabbitListenerEndpoint endpoint, ShardRabbitListener rabbitListener, Object adminTarget) {
        String rabbitAdmin = this.resolve(rabbitListener.admin());
        if (StringUtils.hasText(rabbitAdmin)) {
            Assert.state(this.beanFactory != null, "BeanFactory must be set to resolve RabbitAdmin by bean name");

            try {
                endpoint.setAdmin((AmqpAdmin)this.beanFactory.getBean(rabbitAdmin, RabbitAdmin.class));
            } catch (NoSuchBeanDefinitionException var6) {
                throw new BeanInitializationException("Could not register rabbit listener endpoint on [" + adminTarget + "], no " + RabbitAdmin.class.getSimpleName() + " with id '" + rabbitAdmin + "' was found in the application context", var6);
            }
        }

    }

    @Nullable
    private RabbitListenerContainerFactory<?> resolveContainerFactory(ShardRabbitListener rabbitListener, Object factoryTarget, String beanName) {
        RabbitListenerContainerFactory<?> factory = null;
        String containerFactoryBeanName = this.resolve(rabbitListener.containerFactory());
        if (StringUtils.hasText(containerFactoryBeanName)) {
            this.assertBeanFactory();

            try {
                factory = (RabbitListenerContainerFactory)this.beanFactory.getBean(containerFactoryBeanName, RabbitListenerContainerFactory.class);
            } catch (NoSuchBeanDefinitionException var7) {
                throw new BeanInitializationException(this.noBeanFoundMessage(factoryTarget, beanName, containerFactoryBeanName, RabbitListenerContainerFactory.class), var7);
            }
        }

        return factory;
    }

    private void resolveExecutor(MethodRabbitListenerEndpoint endpoint, ShardRabbitListener rabbitListener, Object execTarget, String beanName) {
        String execBeanName = this.resolve(rabbitListener.executor());
        if (StringUtils.hasText(execBeanName)) {
            this.assertBeanFactory();

            try {
                endpoint.setTaskExecutor((TaskExecutor)this.beanFactory.getBean(execBeanName, TaskExecutor.class));
            } catch (NoSuchBeanDefinitionException var7) {
                throw new BeanInitializationException(this.noBeanFoundMessage(execTarget, beanName, execBeanName, TaskExecutor.class), var7);
            }
        }

    }

    private void resolvePostProcessor(MethodRabbitListenerEndpoint endpoint, ShardRabbitListener rabbitListener, Object target, String beanName) {
        String ppBeanName = this.resolve(rabbitListener.replyPostProcessor());
        if (StringUtils.hasText(ppBeanName)) {
            this.assertBeanFactory();

            try {
                endpoint.setReplyPostProcessor((ReplyPostProcessor)this.beanFactory.getBean(ppBeanName, ReplyPostProcessor.class));
            } catch (NoSuchBeanDefinitionException var7) {
                throw new BeanInitializationException(this.noBeanFoundMessage(target, beanName, ppBeanName, ReplyPostProcessor.class), var7);
            }
        }

    }

    protected void assertBeanFactory() {
        Assert.state(this.beanFactory != null, "BeanFactory must be set to obtain container factory by bean name");
    }

    protected String noBeanFoundMessage(Object target, String listenerBeanName, String requestedBeanName, Class<?> expectedClass) {
        return "Could not register rabbit listener endpoint on [" + target + "] for bean " + listenerBeanName + ", no '" + expectedClass.getSimpleName() + "' with id '" + requestedBeanName + "' was found in the application context";
    }

    private String getEndpointId(ShardRabbitListener rabbitListener) {
        return StringUtils.hasText(rabbitListener.id()) ? this.resolve(rabbitListener.id()) : "org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#" + this.counter.getAndIncrement();
    }

    private String[] getQueues(ShardRabbitListener rabbitListener) {
        int shardSize = Integer.parseInt((String)this.resolveExpression(rabbitListener.shardSize()));
        String queueNamePrefix = (String)this.resolveExpression(rabbitListener.queueNamePrefix());
        List<String> queueList = new ArrayList();

        for(int i = 1; i <= shardSize; ++i) {
            String queue = queueNamePrefix + "_" + i;
            queueList.add(queue);
        }

        String[] queues = (String[])queueList.toArray(new String[0]);
        return queues;
    }

    private void resolveAsString(Object resolvedValue, List<String> result, boolean canBeQueue, String what) {
        Object resolvedValueToUse = resolvedValue;
        if (resolvedValue instanceof String[]) {
            resolvedValueToUse = Arrays.asList((String[])((String[])resolvedValue));
        }

        if (canBeQueue && resolvedValueToUse instanceof Queue) {
            result.add(((Queue)resolvedValueToUse).getName());
        } else if (resolvedValueToUse instanceof String) {
            result.add((String)resolvedValueToUse);
        } else {
            if (!(resolvedValueToUse instanceof Iterable)) {
                throw new IllegalArgumentException(String.format("@RabbitListener." + what + " can't resolve '%s' as a String[] or a String " + (canBeQueue ? "or a Queue" : ""), resolvedValue));
            }

            Iterator var6 = ((Iterable)resolvedValueToUse).iterator();

            while(var6.hasNext()) {
                Object object = var6.next();
                this.resolveAsString(object, result, canBeQueue, what);
            }
        }

    }

    private String[] registerBeansForDeclaration(ShardRabbitListener rabbitListener) {
        List<String> queues = new ArrayList();
        if (this.beanFactory instanceof ConfigurableBeanFactory) {
            QueueBinding[] var3 = rabbitListener.bindings();
            int var4 = var3.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                QueueBinding binding = var3[var5];
                String queueName = this.declareQueue(binding.value());
                queues.add(queueName);
                this.declareExchangeAndBinding(binding, queueName);
            }
        }

        return (String[])queues.toArray(new String[queues.size()]);
    }

    private String declareQueue(org.springframework.amqp.rabbit.annotation.Queue bindingQueue) {
        String queueName = (String)this.resolveExpression(bindingQueue.value());
        boolean isAnonymous = false;
        if (!StringUtils.hasText(queueName)) {
            queueName = Base64UrlNamingStrategy.DEFAULT.generateName();
            isAnonymous = true;
        }

        Queue queue = new Queue(queueName, this.resolveExpressionAsBoolean(bindingQueue.durable(), !isAnonymous), this.resolveExpressionAsBoolean(bindingQueue.exclusive(), isAnonymous), this.resolveExpressionAsBoolean(bindingQueue.autoDelete(), isAnonymous), this.resolveArguments(bindingQueue.arguments()));
        queue.setIgnoreDeclarationExceptions(this.resolveExpressionAsBoolean(bindingQueue.ignoreDeclarationExceptions()));
        ((ConfigurableBeanFactory)this.beanFactory).registerSingleton(queueName + ++this.increment, queue);
        if (bindingQueue.admins().length > 0) {
            queue.setAdminsThatShouldDeclare((Object[])bindingQueue.admins());
        }

        queue.setShouldDeclare(this.resolveExpressionAsBoolean(bindingQueue.declare()));
        return queueName;
    }

    private String declareQueue(String queueName) {
        Queue queue = new Queue(queueName, true, false, false);
        queue.setIgnoreDeclarationExceptions(true);
        if (!this.beanFactory.containsBean(queueName)) {
            ((ConfigurableBeanFactory)this.beanFactory).registerSingleton(queueName, queue);
        }

        queue.setShouldDeclare(true);
        return queueName;
    }

    private void declareExchangeAndBinding(QueueBinding binding, String queueName) {
        Exchange bindingExchange = binding.exchange();
        String exchangeName = this.resolveExpressionAsString(bindingExchange.value(), "@Exchange.exchange");
        Assert.isTrue(StringUtils.hasText(exchangeName), () -> {
            return "Exchange name required; binding queue " + queueName;
        });
        String exchangeType = this.resolveExpressionAsString(bindingExchange.type(), "@Exchange.type");
        ExchangeBuilder exchangeBuilder = new ExchangeBuilder(exchangeName, exchangeType);
        if (this.resolveExpressionAsBoolean(bindingExchange.autoDelete())) {
            exchangeBuilder.autoDelete();
        }

        if (this.resolveExpressionAsBoolean(bindingExchange.internal())) {
            exchangeBuilder.internal();
        }

        if (this.resolveExpressionAsBoolean(bindingExchange.delayed())) {
            exchangeBuilder.delayed();
        }

        if (this.resolveExpressionAsBoolean(bindingExchange.ignoreDeclarationExceptions())) {
            exchangeBuilder.ignoreDeclarationExceptions();
        }

        if (!this.resolveExpressionAsBoolean(bindingExchange.declare())) {
            exchangeBuilder.suppressDeclaration();
        }

        if (bindingExchange.admins().length > 0) {
            exchangeBuilder.admins((Object[])bindingExchange.admins());
        }

        Map<String, Object> arguments = this.resolveArguments(bindingExchange.arguments());
        if (!CollectionUtils.isEmpty(arguments)) {
            exchangeBuilder.withArguments(arguments);
        }

        org.springframework.amqp.core.Exchange exchange = exchangeBuilder.durable(this.resolveExpressionAsBoolean(bindingExchange.durable())).build();
        ((ConfigurableBeanFactory)this.beanFactory).registerSingleton(exchangeName + ++this.increment, exchange);
        this.registerBindings(binding, queueName, exchangeName, exchangeType);
    }

    private void registerBindings(QueueBinding binding, String queueName, String exchangeName, String exchangeType) {
        Object routingKeys;
        if (!exchangeType.equals("fanout") && binding.key().length != 0) {
            int length = binding.key().length;
            routingKeys = new ArrayList();

            for(int i = 0; i < length; ++i) {
                this.resolveAsString(this.resolveExpression(binding.key()[i]), (List)routingKeys, false, "@QueueBinding.key");
            }
        } else {
            routingKeys = Collections.singletonList("");
        }

        Map<String, Object> bindingArguments = this.resolveArguments(binding.arguments());
        boolean bindingIgnoreExceptions = this.resolveExpressionAsBoolean(binding.ignoreDeclarationExceptions());
        boolean declare = this.resolveExpressionAsBoolean(binding.declare());

        Binding actualBinding;
        for(Iterator var9 = ((List)routingKeys).iterator(); var9.hasNext(); ((ConfigurableBeanFactory)this.beanFactory).registerSingleton(exchangeName + "." + queueName + ++this.increment, actualBinding)) {
            String routingKey = (String)var9.next();
            actualBinding = new Binding(queueName, DestinationType.QUEUE, exchangeName, routingKey, bindingArguments);
            actualBinding.setIgnoreDeclarationExceptions(bindingIgnoreExceptions);
            actualBinding.setShouldDeclare(declare);
            if (binding.admins().length > 0) {
                actualBinding.setAdminsThatShouldDeclare((Object[])binding.admins());
            }
        }

    }

    private Map<String, Object> resolveArguments(Argument[] arguments) {
        Map<String, Object> map = new HashMap();
        Argument[] var3 = arguments;
        int var4 = arguments.length;

        for(int var5 = 0; var5 < var4; ++var5) {
            Argument arg = var3[var5];
            String key = this.resolveExpressionAsString(arg.name(), "@Argument.name");
            if (StringUtils.hasText(key)) {
                Object value = this.resolveExpression(arg.value());
                Object type = this.resolveExpression(arg.type());
                Class typeClass;
                String typeName;
                if (type instanceof Class) {
                    typeClass = (Class)type;
                    typeName = typeClass.getName();
                } else {
                    Assert.isTrue(type instanceof String, () -> {
                        return "Type must resolve to a Class or String, but resolved to [" + type.getClass().getName() + "]";
                    });
                    typeName = (String)type;

                    try {
                        typeClass = ClassUtils.forName(typeName, this.beanClassLoader);
                    } catch (Exception var13) {
                        throw new IllegalStateException("Could not load class", var13);
                    }
                }

                this.addToMap(map, key, value, typeClass, typeName);
            } else if (this.logger.isDebugEnabled()) {
                this.logger.debug("@Argument ignored because the name resolved to an empty String");
            }
        }

        return map.size() < 1 ? null : map;
    }

    private void addToMap(Map<String, Object> map, String key, Object value, Class<?> typeClass, String typeName) {
        if (value.getClass().getName().equals(typeName)) {
            if (typeClass.equals(String.class) && !StringUtils.hasText((String)value)) {
                this.putEmpty(map, key);
            } else {
                map.put(key, value);
            }
        } else if (value instanceof String && !StringUtils.hasText((String)value)) {
            this.putEmpty(map, key);
        } else {
            if (!CONVERSION_SERVICE.canConvert(value.getClass(), typeClass)) {
                throw new IllegalStateException("Cannot convert from " + value.getClass().getName() + " to " + typeName);
            }

            map.put(key, CONVERSION_SERVICE.convert(value, typeClass));
        }

    }

    private void putEmpty(Map<String, Object> map, String key) {
        if (this.emptyStringArguments.contains(key)) {
            map.put(key, "");
        } else {
            map.put(key, (Object)null);
        }

    }

    private boolean resolveExpressionAsBoolean(String value) {
        return this.resolveExpressionAsBoolean(value, false);
    }

    private boolean resolveExpressionAsBoolean(String value, boolean defaultValue) {
        Object resolved = this.resolveExpression(value);
        if (resolved instanceof Boolean) {
            return (Boolean)resolved;
        } else if (resolved instanceof String) {
            String s = (String)resolved;
            return StringUtils.hasText(s) ? Boolean.parseBoolean(s) : defaultValue;
        } else {
            return defaultValue;
        }
    }

    private String resolveExpressionAsString(String value, String attribute) {
        Object resolved = this.resolveExpression(value);
        if (resolved instanceof String) {
            return (String)resolved;
        } else {
            throw new IllegalStateException("The [" + attribute + "] must resolve to a String. Resolved to [" + resolved.getClass() + "] for [" + value + "]");
        }
    }

    private String resolveExpressionAsStringOrInteger(String value, String attribute) {
        if (!StringUtils.hasLength(value)) {
            return null;
        } else {
            Object resolved = this.resolveExpression(value);
            if (resolved instanceof String) {
                return (String)resolved;
            } else if (resolved instanceof Integer) {
                return resolved.toString();
            } else {
                throw new IllegalStateException("The [" + attribute + "] must resolve to a String. Resolved to [" + resolved.getClass() + "] for [" + value + "]");
            }
        }
    }

    private Object resolveExpression(String value) {
        String resolvedValue = this.resolve(value);
        return this.resolver.evaluate(resolvedValue, this.expressionContext);
    }

    private String resolve(String value) {
        return this.beanFactory != null && this.beanFactory instanceof ConfigurableBeanFactory ? ((ConfigurableBeanFactory)this.beanFactory).resolveEmbeddedValue(value) : value;
    }

    private static class BytesToStringConverter implements Converter<byte[], String> {
        private final Charset charset;

        BytesToStringConverter(Charset charset) {
            this.charset = charset;
        }

        public String convert(byte[] source) {
            return new String(source, this.charset);
        }
    }

    private static class ListenerMethod {
        final Method method;
        final ShardRabbitListener[] annotations;

        ListenerMethod(Method method, ShardRabbitListener[] annotations) {
            this.method = method;
            this.annotations = annotations;
        }
    }

    private static class TypeMetadata {
        final ShardRabbitListenerAnnotationBeanPostProcessor.ListenerMethod[] listenerMethods;
        final Method[] handlerMethods;
        final ShardRabbitListener[] classAnnotations;
        static final ShardRabbitListenerAnnotationBeanPostProcessor.TypeMetadata EMPTY = new ShardRabbitListenerAnnotationBeanPostProcessor.TypeMetadata();

        private TypeMetadata() {
            this.listenerMethods = new ShardRabbitListenerAnnotationBeanPostProcessor.ListenerMethod[0];
            this.handlerMethods = new Method[0];
            this.classAnnotations = new ShardRabbitListener[0];
        }

        TypeMetadata(ShardRabbitListenerAnnotationBeanPostProcessor.ListenerMethod[] methods, Method[] multiMethods, ShardRabbitListener[] classLevelListeners) {
            this.listenerMethods = methods;
            this.handlerMethods = multiMethods;
            this.classAnnotations = classLevelListeners;
        }
    }

    private class RabbitHandlerMethodFactoryAdapter implements MessageHandlerMethodFactory {
        private MessageHandlerMethodFactory factory;

        RabbitHandlerMethodFactoryAdapter() {
        }

        public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory rabbitHandlerMethodFactory1) {
            this.factory = rabbitHandlerMethodFactory1;
        }

        public InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method method) {
            return this.getFactory().createInvocableHandlerMethod(bean, method);
        }

        private MessageHandlerMethodFactory getFactory() {
            if (this.factory == null) {
                this.factory = this.createDefaultMessageHandlerMethodFactory();
            }

            return this.factory;
        }

        private MessageHandlerMethodFactory createDefaultMessageHandlerMethodFactory() {
            DefaultMessageHandlerMethodFactory defaultFactory = new DefaultMessageHandlerMethodFactory();
            defaultFactory.setBeanFactory(ShardRabbitListenerAnnotationBeanPostProcessor.this.beanFactory);
            DefaultConversionService conversionService = new DefaultConversionService();
            conversionService.addConverter(new ShardRabbitListenerAnnotationBeanPostProcessor.BytesToStringConverter(ShardRabbitListenerAnnotationBeanPostProcessor.this.charset));
            defaultFactory.setConversionService(conversionService);
            defaultFactory.afterPropertiesSet();
            return defaultFactory;
        }
    }
}
